// #pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )  // 启用会显示不了QDebug的输出

#include <QTranslator>
#include <QResource>
#include "dbdata.h"
#include <QFile>
#include "gtime.h"
#include <QObject>
#include <QCoreApplication>
#include "SerialManage.h"
#include "ShipManageB.h"
#include "mylogger.h"
#include <QDebug>
#include "networkmanage.h"
#include "dbapp.h"
#include "DbSetting.h"
#include "poicheck.h"
#include "playvoice.h"
#include "MsgManage.h"
#include <QTimer>
#include "update/simpleupdate.h"
#include "enetwork.h"
#include "update/updateutil.h"
#include "version.h"
#include "update/stateupdate.h"
#include <QSettings>
#include <QSerialPortInfo>
#include "lightcontrol.h"
#include <QSocketNotifier>
#include "update/usbupdate.h"
#include "machineutil.h"
#include "datapostback.h"
#include "MsgParser.h"
#include "networktask.h"
#include "singleapplication.h"
#include "embeddedinput.h"
#include "gpioinput.h"

using namespace ns_yht;

void initParams();
void createLogger(bool recordAis, bool debug);
void scanUsbUpdate();
void reboot();
void startDaemon();
void setupEmbeddedInput(EmbeddedInput *input);
void setupGpioInput(GpioInput *input);
#ifdef Q_OS_LINUX
int getProcessPidByName(const char *proc_name);
#endif

int main(int argc, char *argv[])
{
    SingleApplication a(argc, argv);

    if (a.isRunning())
        return 0;

    QSettings *setting = new QSettings(QCoreApplication::applicationDirPath() + "/setting.ini", QSettings::IniFormat);
    bool udpOpen = (setting->value("UdpOpen", 0).toInt() > 0);
    int udpPort = setting->value("UdpPort", 11003).toInt();
    bool recordAis = (setting->value("RecordAis", 1).toInt() > 0);
    bool postBack = (setting->value("PostBack", 1).toInt() > 0);
    bool debug = (setting->value("Debug", 0).toInt() > 0);
    int shipType = setting->value("ShipType", 1).toInt();
    MyLogger::developDebug = (setting->value("DevelopDebug", 0).toInt() > 0);
    delete setting;
    new PlayVoice;
    LIGHTTASK->start();
    LIGHTTASK->turnOn(LightColor::Green, true);
    LIGHTTASK->turnOn(LightColor::Red, false);
    LIGHTTASK->turnOn(LightColor::Blue, false);
    createLogger(recordAis, debug);
    initParams();

    scanUsbUpdate();

    // 设置嵌入式按键输入处理
    // EmbeddedInput *embeddedInput = new EmbeddedInput(&a);
    // setupEmbeddedInput(embeddedInput);

    // 设置GPIO按键输入处理
    GpioInput *gpioInput = new GpioInput(&a);
    setupGpioInput(gpioInput);

    ShipManageB::newInstance();
    MSGMANAGE->bindSignals();
    if (GLOG)
    {
        GLOG->info("Software Code: {}    Project Version: {} ", SOFTWARECODE, PROJECT_VERSION);
        GLOG->info("ShipType:{}    UdpOpen:{}   RecordAis:{}   PostBack:{}   Debug:{}   DevelopDebug:{}", shipType, udpOpen, recordAis, postBack, debug, MyLogger::developDebug);
    }
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        if (info.portName().startsWith("NULL"))
            continue;
        SERIALMANAGE->openPort(info.portName(), 38400, false); // 打开串口接收ais数据
    }
    DATAPOSTBACK->setPostBack(postBack);
    MSGPARSER->setPostback(postBack);

    QThread postbackThread;
    DATAPOSTBACK->moveToThreadEx(&postbackThread);
    postbackThread.start();

    // 不用管网络是否连接成功都打开
    if (udpOpen)
        NETWORKMANAGE->openUdpSocket(udpPort); // 通过udp接收ais数据
    NetworkTask networkTask(SOFTWARECODE);
    MSGMANAGE->setNetworkTask(&networkTask);
    networkTask.setShipType(shipType);
    networkTask.startScan();

    startDaemon();
    PLAYVOICE->start();
    return a.exec();
}

void createLogger(bool recordAis, bool debug)
{
    MyLogger::init();
    MyLogger::createLogger(qApp->applicationDirPath().toStdString() + "/logs/", "blackbox", recordAis, debug);
}

void initParams()
{
    GTime::init();
    new DbData;
    new DbApp;
    new PoiCheck;

    DbSetting dbSetting;
    QString machineCode = dbSetting.getSetting(SETTING_MACHINECODE);

    if (machineCode.isEmpty())
    {
        machineCode = MachineUtil::machineCode();
        dbSetting.saveSetting(SETTING_MACHINECODE, machineCode);
    }
    else
        MachineUtil::setMachineCode(machineCode);
}

// 设置嵌入式输入设备
void setupEmbeddedInput(EmbeddedInput *input)
{
    // 添加常见的输入设备
    // 注意：具体设备路径需要根据实际硬件配置调整

    // 添加GPIO按键设备（如果有）
    input->addInputDevice("/dev/input/event0");

    // 添加其他可能的输入设备
    input->addInputDevice("/dev/input/event1");
    input->addInputDevice("/dev/input/event2");

    // 连接按键信号
    QObject::connect(input, &EmbeddedInput::keyPressed, [](int keycode)
                     {
        if (GLOG) {
            GLOG->info("Physical key pressed: keycode={}", keycode);
        }
        
        // 根据按键码执行相应操作
        switch (keycode) {

        case 1://KEY_POWER:
            if (GLOG) {
                GLOG->info("Power key pressed - shutting down system");
            }
            // 可以添加关机逻辑
            break;
        case 2://KEY_VOLUMEDOWN:
            if (GLOG) {
                GLOG->info("Volume down key pressed");
            }
            // 可以添加音量减逻辑
            break;
        case 3://KEY_VOLUMEUP:
            if (GLOG) {
                GLOG->info("Volume up key pressed");
            }
            // 可以添加音量加逻辑
            break;

        default:
            if (GLOG) {
                GLOG->info("Unhandled key: {}", keycode);
            }
            break;
        } });

    QObject::connect(input, &EmbeddedInput::keyReleased, [](int keycode)
                     {
        if (GLOG) {
            GLOG->info("Physical key released: keycode={}", keycode);
        } });
}

// 设置GPIO输入
void setupGpioInput(GpioInput *input)
{
    // 添加GPIO按键（根据实际硬件配置调整GPIO编号）
    // 标号：gpio2_a4   序号：68，这个4只是自己标识的
    input->addGpioButton(68, 4);

    // 连接按键信号
    QObject::connect(input, &GpioInput::keyPressed, [](int keycode)
                     {
                         if (GLOG)
                         {
                             GLOG->info("GPIO key pressed: keycode={}", keycode);
                         }

                         // 根据按键码执行相应操作
                         switch (keycode)
                         {
                         case 4:
                             PLAYVOICE->ensureCurrentMsgModel();
                             break;

                         default:
                             if (GLOG)
                             {
                                 GLOG->info("Unhandled GPIO key: {}", keycode);
                             }
                             break;
                         }
                         //
                     });

    // QObject::connect(input, &GpioInput::keyReleased, [](int keycode)
    //                  {
    //     if (GLOG) {
    //         GLOG->info("GPIO key released: keycode={}", keycode);
    //     } });
}

#if 0
bool checkProcess(QString exePath, QString exeName)
{
    QString str;
#ifdef Q_OS_WIN
    QProcess process;
    QString command = QString("wmic process where \"name='%1'\" get ExecutablePath /value").arg(exeName);
    process.start(command, QIODevice::ReadOnly);

    process.waitForFinished();

    QByteArray result = process.readAllStandardOutput();
    str = QString::fromUtf8(result);
    str.replace("\\", "/");

    return str.contains(exePath, Qt::CaseInsensitive);

#else
    QProcess process;
    QString command = QString("pgrep -f %1").arg(exeName); // 根据进程名查找 PID
    process.start(command);
    process.waitForFinished();

    QByteArray result = process.readAllStandardOutput();
    QString pidStr = QString::fromUtf8(result).trimmed(); //.replace("\\","/");

    QStringList pidList = pidStr.split("\n", QString::SkipEmptyParts); // 本机开启多个同名app，这里会查出来多个id，需要分别查询路径

    if (!pidStr.isEmpty())
    {
        for (auto pid : pidList)
        {
            // 获取进程的可执行文件路径
            command = QString("readlink /proc/%1/exe").arg(pid);
            process.start(command);
            process.waitForFinished();

            result = process.readAllStandardOutput();
            str = QString::fromUtf8(result).trimmed();

            if (str.contains(exePath, Qt::CaseInsensitive))
                return true;
        }
    }
    return false;
#endif
}
#endif

void scanUsbUpdate()
{
    UsbUpdate uu(QCoreApplication::applicationDirPath());
    bool ret = uu.scanUpdate();
    if (ret)
    {
        if (GLOG)
            GLOG->info("start usb update");
        PLAYVOICE->play(PlayVoice::VoiceType::StartSystemUpdate);
        ret = uu.update();
        if (GLOG)
            GLOG->info("usb update finished {}", ret);
        if (ret)
        {
            PLAYVOICE->play(PlayVoice::VoiceType::SystemUpdateSucceed);
            QTimer::singleShot(5000, [=]
                               { reboot(); });
        }
        else
            PLAYVOICE->play(PlayVoice::VoiceType::SystemUpdateFailed);
    }
    else
    {
        PLAYVOICE->play(PlayVoice::VoiceType::SystemStart);
        if (GLOG)
            GLOG->info("no usb update");
    }
}

void reboot()
{
#ifdef Q_OS_LINUX
    QProcess process;
    process.startDetached("reboot", QStringList() << "-f");
    qApp->quit();
#endif
}

void startDaemon()
{
#ifdef Q_OS_LINUX
    int pid = getProcessPidByName("yhtd");
    if (pid != -1)
    {
        if (GLOG)
            GLOG->info("yhtd is running, pid:{}", pid);
    }
    else
    {
        const QString deamon = QCoreApplication::applicationDirPath() + "/yhtd";
        if (QFile::exists(deamon))
            QProcess::startDetached(deamon);
    }
#endif
}
#ifdef Q_OS_LINUX
int getProcessPidByName(const char *proc_name)
{
    FILE *fp;
    char buf[100];
    char cmd[200] = {'\0'};
    int pid = -1;
    sprintf(cmd, "pidof %s", proc_name);
    if ((fp = popen(cmd, "r")) != NULL)
    {
        if (fgets(buf, 255, fp) != NULL)
            pid = atoi(buf);
    }
    pclose(fp);
    return pid;
}
#endif